using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// Tween operand data.
/// </summary>
public class TweenOperandData : System.Object{
	public GameObject tweenTarget;
	public Hashtable paramTable;
}

/// <summary>
/// Virtual machine class.
/// </summary>
public class VirtualMachine : MonoBehaviour {
	
	public class SerializationInfo{
		public int		m_Id;
		public string m_ScenarioName;
		public string m_NodeName;	
	}
	
	public enum Mode{
		DEBUG_MODE,
		NORMAL_MODE,
	}

		#region Virtual Methods	

	virtual public void Run(){}

	virtual public void Run( int entryProgramCounter ){}

	virtual public void GoToLabel( string label ){}

	virtual public void OnUpdate(){}

		#endregion
			
	void Update(){								
		if( update ){
			OnUpdate();
		}

/*				
		if( _LOG_SYMBOLS ){
			_LOG_SYMBOLS = false;
			LogTableKeyAndVal( symbolTable );			
		}		
//*/		
	}
		#region Members

//	public bool		_LOG_SYMBOLS;
	[HideInInspector] public GameObject m_CurrTarget;

	[HideInInspector] public bool _ENABLE_DEBUG_MODE;	
	[HideInInspector] public bool stubIndent;
	
	// const variables.
	[HideInInspector] public const int		_CODE_SIZE = 1;
	[HideInInspector] public const int		_CALL_STACK_CAPACITY = 100;
	[HideInInspector] public const int		_FUNC_DICT_CAPACITY = 100;
	[HideInInspector] public float		kWaitSec			= 1f;	
	[HideInInspector] public bool		_LOG_CODES;
    		
	// --- Used and Serializable ---.
	// Using and Serializable.
  [HideInInspector] public byte[] code;
	[HideInInspector] public bool update = false;	
	[HideInInspector] public bool _IS_FIRST = true;
	[HideInInspector] public bool canTextProgress;
	[HideInInspector] public bool isTweenMoveFinished;	
	[HideInInspector] public int pc = 0;
	[HideInInspector] public int	_SAVE_PC_POS = 0;				
	[HideInInspector] public float m_TimeElapsed = 0f;			
	[HideInInspector] public string	leftHand = "";		// this is VarTable's key.
	[HideInInspector] public string	rightHand = "";		// this is VarTable's value.				
	[HideInInspector] public string messagingTargetName;
	
	// Wait member.
	[HideInInspector] public float	m_ElapsedSec	= 0f;
	[HideInInspector] public float	m_WaitSec		= 0f;	
	[HideInInspector] public int loadedTextLiteralPos = 0;
	
	public Stack<int> callStack = new Stack<int>( _CALL_STACK_CAPACITY  );	// OBSOLETE...

	public TweenOperandData tweenDataCached = new TweenOperandData();		
//	public Dictionary<string,FuncInfo> funcTable = new Dictionary<string, FuncInfo>();// NOT LIMIT _FUNC_DICT_CAPACITY );

	public Hashtable paramHash;

	public  static int TRUE = 1;
	public  static int FALSE = 0;

	public string currentScenarioName{ get; set; }
	public string currNodeName{ get{ return m_CurrNodeName; } }
	public string prevNodeName{ get{ return m_PrevNodeName; } }

		#endregion
		#region Protected Members
							
	protected bool m_MessagePompToMsghandler = false;			
	protected Dictionary<string,int> m_NodePcMap = new Dictionary<string, int>();
	protected string m_CurrNodeName;

	protected string m_PrevNodeName;
	protected System.Text.StringBuilder m_TextBuilder;
	
	// ------------------------------------------------------.
	protected string m_CurrentText;
	protected bool m_IsPlayVoice;
	protected int m_CurrentVoiceID;
	protected string m_CurrentVoiceKey;

		#endregion

		#region Static Members
		static public string saveTag;	
		static public string loadedString = "";
		static public string loadedTextLiteralString = "";
		static public bool autoMode{ get; set; }
		static public bool _SKIP_ALREADY_READ{ get; set; }
		static public float _AUTO_MODE_WAIT_TIME = 2f;//0.2f;
		static public Hashtable symbolTable = new Hashtable();	// TODO : OPTIMIZE Set Capacity.  STATIC .
		static public bool skipAlreadyPass = false;
		static public bool _ALREADY_PASS_THE_NODE = false;
		#endregion


	static public string GetLoadedString(){
		return loadedString;	
	}

		#region Public Methods

	public void Init(int codeSize ) {
        code = new byte[codeSize];
		AsignCodeOpcodeNull();
		
		// this is variable Table.
		symbolTable = new Hashtable();		
		
		m_TextBuilder = new System.Text.StringBuilder();
	}

	public void SetCode( byte[] bytes ){
		code =	bytes;
	}

	public void AppendToStringBuilder( string str ){
		m_TextBuilder.Append( str );	
	}
	
	public void ClearTextBuilder(){
		m_TextBuilder.Length = 0;
	}
	
	public string ToStringBuilder( ){
		return m_TextBuilder.ToString();
	}
	
	public void AddSymbol( string key , Hashtable param  ){
		symbolTable[ key ] = param;
	}
	
	public void EnableAutoMode( bool t ){
		autoMode = t;
	}
	
	public void ClearCode( ){
		code = new 	byte[ 1 ];
		code[ 0] = Opcode.NULL;
	}

	public SerializationInfo Serialize(){
		SerializationInfo info = new SerializationInfo();
		
		info.m_NodeName = m_CurrNodeName;
		info.m_ScenarioName = currentScenarioName;		
//		ViNoDebugger.Log( "SaveInfo" , "Saved Node:" + m_CurrNodeName );		

		return info;
	}

	public void Deserialize( string nodeName ){
		m_CurrNodeName = nodeName;		
	}		
	
	public void UpdateMessageVoiceData( string voiceKey , int voiceID ){
		m_IsPlayVoice = true;
		m_CurrentVoiceID = voiceID;
		m_CurrentVoiceKey = voiceKey;
	}

	public void EnableUpdate( bool t ){
		update = t;	
	}
	
	public void ProgressProgramCounter( int i ){
		pc += i;	
	}
		#endregion

		#region Private Methods
	
	/// <summary>
	/// Raises the activate auto skip event.
	/// </summary>
	/// <param name='t'>
	/// T.
	/// </param>
	void OnActivateAutoSkip( bool t ){
		autoMode = t;
	}		

	private void AsignCodeOpcodeNull(){
		for( int i=0;i<code.Length;i++){
			code[ i ] = Opcode.NULL;	
		}		
	}

	private void LogTableKeyAndVal( Hashtable tbl ){
		foreach( string key in tbl.Keys ){
//			ViNoDebugger.Log( "Key :" + key + " and val : " + tbl[ key ] );	
			Hashtable innerHash = tbl[ key ] as Hashtable;
			if( innerHash != null ){
				LogTableKeyAndVal( innerHash );	
			}
		}			
	}

	/// <summary>
	/// Raises the tween move finished event.
	/// </summary>
	private void OnTweenMoveFinished( ){
		 isTweenMoveFinished = true;
	}
		#endregion

		#region Getter
	
	public int GetCodeLength(){
		if( code != null ){
			return code.Length;
		}
		else{
			return 0;
		}
	}
	
	public bool IsFinish(){
		if( pc >= code.Length ){
			if( _IS_FIRST ){
				_IS_FIRST = false;
				pc = 0;
				return true;
			}else{
				update = false;
			}
			return true;
		}			
		return false;
	}

	public bool IsUpdate(){
		return update;	
	}

	// If the given key does not exist in nodepcmap , return -1 .
	public int GetNodePcPointByKey( string key ){
		if( m_NodePcMap.ContainsKey( key ) ){
			return 	m_NodePcMap[ key ];
		}
		else{			
			return -1;	
		}
	}	

	public Dictionary<string,int>.KeyCollection GetNodeKeys(){
		return m_NodePcMap.Keys;
	}

	public string[] GetLabels(){
		List<string> labelList = new List<string>();
		foreach( string key in m_NodePcMap.Keys ){
			labelList.Add( key );
		}
		return labelList.ToArray();
	}	

	public object GetSymbolObject( string key ){
		if( symbolTable.ContainsKey( key ) ){ 
			return symbolTable[ key ];
		}else{
			return null;
		}
	}

		#endregion

		#region Coroutine Methods

	IEnumerator SendEnableUpdateVMDelayed( float time ){
		yield return new WaitForSeconds( time );
		
		EnableUpdate( true );
	}

		#endregion

}
